#include <zlib.h>
#include <time.h>
#include <fcntl.h>
#include <netdb.h>
#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>
#include <syslog.h>
#include <pthread.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/select.h>
#include <netinet/in.h>
#include "err_quit.h"
#include "http.h"
#include "list.h"
#include "safe_free.h"
#include "include.h"
#include "util_debug.h"

/* system may failed somtimes, wo need to change signal behavior of SIG_CHLD */
typedef void(*sighandler_t)(int);

int http_decode(const char *http_file, const char *decode_file);
int read_process_forward(int fd_from, int fd_to);
int process_none(int fd_to, http_header_t *header);
int process_txt_len(int fd_from, int fd_to, http_header_t *header, int len, int encd);
int process_txt_len_flate(int fd_to, http_header_t *header, unsigned char *body, int len);
int process_txt_len_encd(int fd_to, http_header_t *header, unsigned char *body, int len);
int process_txt_chk(int fd_from, int fd_to, http_header_t *header, int encd);
int process_txt_chk_flate(int fd_from, int fd_to, http_header_t *header);
int process_txt_chk_encd(int fd_from,int fd_to, http_header_t *header);
int process_txt_chk_encd_keep(int fd_to, http_header_t *header, struct list_head *head);
int process_txt_chk_encd_transfer(int fd_to, http_header_t *header, const char *body, int size);
int process_txt_none(int fd_from, int fd_to, http_header_t *header, int encd);
int process_txt_none_encd(int fd_from, int fd_to, http_header_t *header);
int process_txt_none_flate(int fd_from, int fd_to, http_header_t *header);
int read_forward_chunk(int fd_from, int fd_to, int is_txt);
int forward_http_chunk_list(int fd, struct list_head *head);
int forward_http_chunk(int fd, http_chunk_t *chunk);
int forward_txt_none(int fd, http_header_t *header, unsigned char *body, int len, int whole);
int get_gunzip(unsigned char *src, unsigned int len_s, char **dst, unsigned int *len_d);

int main(int argc, char **argv)
{
    if(argc != 2) {
        printf("http_decode xxx.http\n");
        return 0;
    }
    char *http_file = argv[1];
    char decode_file[128] = {0};
    snprintf(decode_file, sizeof(decode_file) - 1, "%s.dec.http", http_file);
    http_decode(http_file, decode_file);

    return 0;
}

int http_decode(const char *http_file, const char *decode_file)
{
    int fd_from = open(http_file, O_RDONLY, 0444);
    if(fd_from < 0) {
        debug_err("open(%s) in %s: %s\n", http_file, __func__, strerror(errno));
        return -1;
    }

    int fd_to = open(decode_file, O_RDWR | O_CREAT | O_TRUNC, 0666);
    if(fd_to < 0) {
        debug_err("open(%s) in %s: %s\n", decode_file, __func__, strerror(errno));
        close(fd_from);
        return -1;
    }

    while(1) {
        int ret = read_process_forward(fd_from, fd_to);
        if(ret <= 0) {
            break;
        }
    }
    close(fd_from);
    close(fd_to);
}


/*
 * return: 
 *  <= 0 : failed
 *  > 0  : ok
 *  第一次调用时:(肯定是request)
 *      会在函数内解析header
 *      然后connect到真正服务器的地址，保存fd_to, ssl_to(https), regex
 *      根据服务器地址确定正则表达式
 *  第二次调用就是response
 *
 *  之后的每次调用都是一次request，一次response
 */
int read_process_forward(int fd_from, int fd_to)
{
    int  pr;
    long long int  len;
    int  ret;
    int  encd;
    int  req_or_rsp;
    char buff_header[LEN_HEADER] = {0};

    /* 1. 读http头 */
    ret = read_http_header(fd_from, buff_header, sizeof(buff_header) - 1);
    if(ret < 0) {
        debug_err("%s: read_http_header() failed\n", __func__);
        return ret;
    }
    else if(ret == 0) {
        debug_err("%s: finish all read\n", __func__);
        return ret;
    }

    /* 2. 解析http头 */
    http_header_t header;
    memset(&header, 0, sizeof(http_header_t));
    init_list_head(&(header.head));

    if(parse_http_header(buff_header, &header) < 0) {
        debug_err("%s: parse_http_header() failed\n", __func__);
        return -1;
    }

    req_or_rsp = is_http_req_rsp(&header);

    /* 6. 解析优先级，编码，长度信息 */
    len = get_pr_encd(&(header.head), &pr, &encd, req_or_rsp);
    //printf("len = %lld, pr = %d, encd = %d\n", len, pr, encd);
    /* 7. 根据优先级替换转发 */
    switch(pr) {
    case PR_TXT_LEN:
        ret = process_txt_len(fd_from, fd_to, &header, len, encd);
        if(ret != ERR_BODY_MEM) {
            if(ret < 0) {
                debug_err("%s: process_txt_len() ERR_BODY_MEM\n", __func__);
            }
            break;
        }
    case PR_TXT_CHUNK:
        ret = process_txt_chk(fd_from, fd_to, &header, encd);
        break;
    case PR_TXT_NONE:
        ret = process_txt_none(fd_from, fd_to, &header, encd);
        break;
    case PR_NONE:
    default:
        ret = process_none(fd_to, &header);
        break;
    }
    free_http_header(&header);
    return ret;
}

int process_none(int fd_to, http_header_t *header)
{
    debug_print("%s: start\n", __func__);
    int  ret;
    char buff_header[LEN_HEADER] = {0};
    http_header_tostr(header, buff_header);
    ret = my_write(fd_to, "ld", strlen(buff_header), buff_header);
    return ret<0?ret:1;
}

/*
 * return:
 *      ok      : 1
 *      failed  : <=0, ERR_BODY_MEM 
 */ 
int process_txt_len(int fd_from, int fd_to, http_header_t *header, int len, int encd)
{
    debug_print("%s: start\n", __func__);
    int  ret;
    char buff_header[LEN_HEADER] = {0};
    if(len <= 0) {
        http_header_tostr(header, buff_header);
        return my_write(fd_to, "ld", strlen(buff_header), buff_header);
    }

    unsigned char *body = (unsigned char *)calloc(1, len + 1);
    if(NULL == body) {
        return ERR_BODY_MEM;
    }

    ret = readn(fd_from, body, len);
    if(ret <= 0) {
        SAFE_FREE(body);
        return  ret;
    }
    if(encd == ENCD_NONE) {
        ret = process_txt_len_flate(fd_to, header, body, len);
    }
    else {
        ret = process_txt_len_encd(fd_to, header, body, len);
    }
    SAFE_FREE(body);
    return ret;
}

/*
 * return:
 *  ok     : 1  
 *  failed : -1
 */
int process_txt_len_flate(int fd_to, http_header_t *header, unsigned char *body, int len)
{
    /* 网页未压缩 */
    debug_print("%s: start\n", __func__);
    char buff_header[LEN_HEADER] = {0};
        http_header_tostr(header, buff_header);
    http_header_tostr(header, buff_header);
    int mywr = my_write(fd_to, "ldld", strlen(buff_header), buff_header, len, body);
    if(mywr < 0) {
        return -1;
    }
    return 1;
}

/*
 * return:
 *      ok      : 1
 *      failed  : -1
 */
int process_txt_len_encd(int fd_to, http_header_t *header, unsigned char *body, int len)
{
    /* 网页压缩,获取解压内容 */
    debug_print("%s: start\n", __func__);
    int mywr;
    int ret = -1;
    unsigned int  len_gunzip = 0;
    unsigned char *gunzip = NULL;
    char buff_header[LEN_HEADER] = {0};
    ret = get_gunzip(body, len, &gunzip, &len_gunzip);
    if(ret == 0){
        /* 解压成功 */
        rewrite_clen_encd(&(header->head), len_gunzip, ENCD2FLATE);
        http_header_tostr(header, buff_header);
        mywr = my_write(fd_to, "ldld", strlen(buff_header), buff_header, len_gunzip, (char *)gunzip);
        SAFE_FREE(gunzip);
        if(mywr < 0) {
            return -1;
        }
    }
    else {
        /* 解压失败 */
        http_header_tostr(header, buff_header);
        mywr = my_write(fd_to, "ldld", strlen(buff_header), buff_header, len, body);
        if(mywr < 0) {
            return -1;
        }
    }
    return 1;
}


/*
 * return:
 *      ok      : 1
 *      failed  : <=0
 */
int process_txt_chk(int fd_from, int fd_to, http_header_t *header, int encd)
{
    debug_print("%s: start\n", __func__);
    return encd==ENCD_NONE?\
                process_txt_chk_flate(fd_from, fd_to, header):\
                process_txt_chk_encd(fd_from, fd_to, header);
}

/*
 * 循环读取chunk,替换,转发
 * return:
 *      ok      : 1
 *      failed  : <=0
 */
int process_txt_chk_flate(int fd_from, int fd_to, http_header_t *header)
{
    debug_print("%s: start\n", __func__);
    int  ret;
    char buff_header[LEN_HEADER] = {0};
    http_header_tostr(header, buff_header);
    ret = my_write(fd_to, "ld", strlen(buff_header), buff_header);
    if(ret < 0) {
        return -1;
    }
    ret = read_forward_chunk(fd_from, fd_to, 1);
    return ret<=0?ret:1;
}

/*
 * return:
 *      ok      : 1
 *      failed  : <=0
 */
int process_txt_chk_encd(int fd_from,int fd_to, http_header_t *header)
{
    debug_print("%s: start\n", __func__);
    int  ret;
    unsigned int len_chunk  = 0;
    unsigned int len_gunzip = 0;
    unsigned char *gunzip = NULL;
    unsigned char *all_chunk = NULL;
    struct list_head chunk_head;
    init_list_head(&chunk_head);
    ret = read_all_chunk(fd_from, &chunk_head);               /* calloc chunk_head*/
    if(ret <= 0) {
        syslog(LOG_INFO, "read_all_chunk failed\n");
        return ret;
    }

    ret = http_all_chunk_to_buff(&chunk_head, &all_chunk, &len_chunk);  /* calloc all_chunk*/
    if(ret < 0) {
        syslog(LOG_INFO, "组装chunk失败");
        ret = process_txt_chk_encd_keep(fd_to, header, &chunk_head);
        if(ret <= 0) {
            goto end;
        }
    }
    else {
        ret = get_gunzip(all_chunk, len_chunk, &gunzip, &len_gunzip);   /* calloc gunzip */
        SAFE_FREE(all_chunk);                                           /* free all_chunk */
        if(ret == 0) {
            /* 解压成功 */
            ret = process_txt_chk_encd_transfer(fd_to, header, (char *)gunzip, len_gunzip);
            SAFE_FREE(gunzip);                                          /* free gunzip */
            if(ret <= 0){
                goto end;
            }
        }
        else {
            /* 解压失败 */
            ret = process_txt_chk_encd_keep(fd_to, header, &chunk_head);
            if(ret <= 0) {
                goto end;
            }
        }
    }
    free_chunk_list(&chunk_head);  /* ok */
    return 1;
end:
    free_chunk_list(&chunk_head);  /* failed */
    return -1;
}

/*
 * return:
 *      ok      : 1
 *      failed  : -1
 */
int process_txt_chk_encd_keep(int fd_to, http_header_t *header, struct list_head *head)
{
    debug_print("%s: start\n", __func__);
    int ret;
    char buff_header[LEN_HEADER] = {0};
    http_header_tostr(header, buff_header);
    ret = my_write(fd_to,"ld", strlen(buff_header), buff_header);
    if(ret < 0) {
        return -1;
    }
    ret = forward_http_chunk_list(fd_to, head);
    return ret<0?-1:1;
}


/*
 * return:
 *      ok      : 1
 *      failed  : -1
 */
int process_txt_chk_encd_transfer(int fd_to, http_header_t *header, const char *body, int size)
{
    debug_print("%s: start\n", __func__);
    int  ret;
    char buff_header[LEN_HEADER] = {0};
    char chunk_size[64] = {0};
    rewrite_encd(&(header->head), ENCD2FLATE);
    sprintf(chunk_size, "%x\r\n", size);
    http_header_tostr(header, buff_header);
    ret = my_write(fd_to, "ldldldld", strlen(buff_header), buff_header, 
            strlen(chunk_size), chunk_size, size, body, 7, "\r\n0\r\n\r\n");
    return ret<0?-1:1;
}

/*
 * return:
 *      ok      : 0
 *      failed  : -1
 */
int process_txt_none(int fd_from, int fd_to, http_header_t *header, int encd)
{
    /* 只可能是connection-close */
    debug_print("%s: start\n", __func__);
    int ret;
    if(encd == ENCD_NONE) {
        ret = process_txt_none_flate(fd_from, fd_to, header);
    }
    else {
        ret = process_txt_none_encd(fd_from, fd_to, header);
    }
    return ret;
}

int process_txt_none_encd(int fd_from, int fd_to, http_header_t *header)
{
    debug_print("%s: start\n", __func__);
    int  ret;
    int  mywr;
    int  whole = 1;
    int  offset = 0;
    int  real_read;
    int  no_body = 1;
    char buff_header[LEN_HEADER] = {0};
    unsigned char body[LEN_SSL_RECORD] = {0};
    int ava = sizeof(body);
    /* 如果有body，那么此body一定是结束body */
    while(1) {
        real_read = read(fd_from, body + offset, ava);
        if(real_read < 0) {
            perror("pr_txt_none: read()");
            return -1;
        }
        else if(real_read == 0) {
            if(no_body) {
                http_header_tostr(header, buff_header);
                mywr = my_write(fd_to, "ld", strlen(buff_header), buff_header);
                if(mywr < 0) {
                    return -1;
                }
            }
            /* 替换转发 */
            if(offset > 0) {
                ret = forward_txt_none(fd_to, header, body, offset, whole);
                if(ret < 0) {
                    return -1;
                }
            }
            return 0;
        }
        else {
            no_body = 0;
            ava    -= real_read;
            offset += real_read;
            if(ava == 0) {
                offset = 0;
                ava = sizeof(body);
                if(whole == 1) {
                    http_header_tostr(header, buff_header);
                    mywr = my_write(fd_to, "ld", strlen(buff_header), buff_header);
                    if(mywr < 0) {
                        return -1;
                    }
                }
                whole = 0;
                ret = forward_txt_none(fd_to, header, body, offset, whole);
                if(ret < 0) {
                    return -1;
                }
                memset(body, 0, sizeof(body));  //unnecessary
            }
        }
    }
    return 1;
}

int process_txt_none_flate(int fd_from, int fd_to, http_header_t *header)
{
    debug_print("%s: start\n", __func__);
    int  ret;
    char buff_header[LEN_HEADER] = {0};
    http_header_tostr(header, buff_header);
    ret = my_write(fd_to, "ld", strlen(buff_header), buff_header);
    if(ret <= 0) {
        return -1;
    }
    char body[LEN_SSL_RECORD] = {0};
    while((ret = read(fd_from, body, sizeof(body)-1)) > 0) {
        if(my_write(fd_to, "ld", ret, body) < 0){
            return -1;
        }
        memset(body, 0, sizeof(body));
    }
    return ret;
}

int read_forward_chunk(int fd_from, int fd_to, int is_txt)
{
    debug_print("%s: start\n", __func__);
    while(1) {
        int ret;
        http_chunk_t chunk;
        memset(&chunk, 0, sizeof(chunk));
        ret = read_parse_chunk(fd_from, &chunk);
        if(ret <= 0) {
            free_http_chunk(&chunk);
            return ret;
        }
        ret = forward_http_chunk(fd_to, &chunk);
        if(ret <= 0) {
            return ret;
        }
        int size = chunk.chk_size;
        free_http_chunk(&chunk);
        if(size <= 0) {
            break;
        }
    }

#ifdef FUNC
    printf("==========finish read_forward_chunk()==========\n");
#endif
    return 1;
}



int forward_http_chunk_list(int fd, struct list_head *head)
{
    debug_print("%s: start\n", __func__);
    int ret = -1;
    struct list_head *pos;
    list_for_each(pos, head){
        http_chunk_t *chunk = list_entry(pos, http_chunk_t, list);
        ret = forward_http_chunk(fd, chunk);
        if(ret <= 0) {
            return ret;
        }
    }
    return ret;
}

int forward_http_chunk(int fd, http_chunk_t *chunk)
{
    debug_print("%s: start\n", __func__);
    int  ret;
    unsigned int  len;
    unsigned char *buff = NULL;
    http_chunk_to_buff(chunk, &buff, &len);
    ret = my_write(fd, "ld", len, buff); 
    SAFE_FREE(buff);
    return ret;
}


int forward_txt_none(int fd, http_header_t *header, unsigned char *body, int len, int whole)
{
    debug_print("%s: start\n", __func__);
    int ret;
    char buff_header[LEN_HEADER] = {0};
    int mywr;

    /* 不完整的包不用转header */
    if(whole != 1) {
        mywr = my_write(fd, "ld", len, body);
        if(mywr < 0) {
            return -1;
        }
    }
    else {
        /* 整包解压 */
        unsigned int  len_gunzip;
        unsigned char *gunzip = NULL;
        ret = get_gunzip(body, len, &gunzip, &len_gunzip);
        if(ret < 0) {
            http_header_tostr(header, buff_header);
            mywr = my_write(fd, "ldld", strlen(buff_header), buff_header, len, body); 
            if(mywr < 0) {
                return -1;
            }
        }
        else {
            rewrite_encd(&(header->head), ENCD2FLATE);
            http_header_tostr(header, buff_header);
            mywr = my_write(fd, "ldld", strlen(buff_header), buff_header, len_gunzip, gunzip);
            SAFE_FREE(gunzip);
            if(mywr < 0) {
                return -1;
            }
        }
    }

    return 1;
}


int http_gunzip(unsigned char *source, unsigned int s_len, unsigned char **dest, unsigned int *d_len, int gzip)
{ 
    debug_print("%s: start\n", __func__);
    /* 这段代码暂时还是有问题 */
    int ret; 
    unsigned have; 
    z_stream strm; 
    unsigned char out[CHUNK]; 
    int totalsize = 0; 

    /* both inflateInit and inflateInit2　require */ 
    strm.zalloc = Z_NULL;
    strm.zfree  = Z_NULL;
    strm.opaque = Z_NULL;

    /* inflateInit2() requires */
    strm.avail_in = 0; 
    strm.next_in = Z_NULL;

    if(gzip) 
        ret = inflateInit2(&strm, 47);    /* don't know why must be 47 */ 
    else 
        ret = inflateInit(&strm);

    if (ret != Z_OK)
        return ret; 

    strm.avail_in = s_len; 
    strm.next_in = source; 

    /* run inflate() on input until output buffer not full */ 
    do { 
        strm.avail_out = CHUNK; 
        strm.next_out = out; 
        ret = inflate(&strm, Z_NO_FLUSH); 
        assert(ret != Z_STREAM_ERROR);  /* state not clobbered */ 

        switch (ret) { 
        case Z_NEED_DICT: 
            ret = Z_DATA_ERROR;     // and fall through
        case Z_DATA_ERROR: 
        case Z_MEM_ERROR: 
            inflateEnd(&strm); 
            return ret; 
        } 

        have = CHUNK - strm.avail_out; 
        totalsize += have; 
        *dest = realloc(*dest, totalsize);
        printf("*dest=%p\n", *dest);
        memcpy(*dest + totalsize - have, out, have); 
    } while (strm.avail_out == 0);
    //printf("*dest=%p\n", *dest);
    *d_len  = totalsize;

    /* clean up and return */ 
    (void)inflateEnd(&strm);
    printf("*dest=%p\n", *dest);
    return ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR;
}

int get_gunzip(unsigned char *src, unsigned int len_s, char **dst, unsigned int *len_d)
{
    int ret;
    debug_print("%s: start\n", __func__);
    ret = http_gunzip(src, len_s, dst, len_d, ENCD2FLATE);
    //printf("ret = %d, len_s = %d, *len_d = %d *dst=%p\n", ret == Z_OK?0:-1, len_s, *len_d, *dst);
    //printf("gunzip = [%s]\n", *dst);
    return ret == Z_OK?0:-1;
}


